/**
 * 
 */
package cn.com.easy.utils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;

import cn.com.easy.dto.MessageDTO;
import cn.com.easy.exception.BusinessException;

import com.github.cage.Cage;
import com.github.cage.GCage;

/**
 * 用于记录会话的工具体
 * 
 * @author nibili 2015年8月28日下午2:40:24
 * 
 */
public class CaptchaUtils {

	/** 邮箱动态验证码 */
	private static final String MAIL_CAPTCHA_TAG = "MAIL_CAPTCHA_TAG";
	/** 验证码session key */
	private static final String CAPTCHA_TAG = "CAPTCHA_TAG";
	/** 生成验证码的类 */
	private static final Cage cage = new GCage();
	/** 验证码有效期十分钟 */
	private static GuavaCacheUtils guavaCacheUtils = GuavaCacheUtils.newInstance(10 * 60);
	/** 同一个手机号一分钟内连续发送验证码的次数 */
	private static GuavaCacheUtils guavaCacheSendCountUtils = GuavaCacheUtils.newInstance(1 * 60);

	/**
	 * 发送邮箱验证码，回调函数
	 * 
	 * @author nibili 2017年4月27日
	 * 
	 */
	public static interface SendMailCaptchaCallBack {
		/**
		 * 这里添加发送手机验证码
		 * 
		 * @param captcha
		 * @author nibili 2017年4月27日
		 */
		public void sendCaptchaToMail(String captcha) throws Exception;
	}

	/**
	 * 获取一个邮箱验证码，并放到session中
	 * 
	 * @param request
	 * @return
	 * @author nibili 2016年6月20日
	 */
	public static void sendMailCaptcha(String mail, int sendTotalPerMinute, SendMailCaptchaCallBack sendMailCaptchaCallBack) throws Exception {
		if (ValidUtils.IsEmail(mail) == false) {
			throw new BusinessException("请正确填写邮箱");
		}
		// 验证十分钟内连续发送验证码次数
		{
			Integer count = guavaCacheSendCountUtils.get(mail, Integer.class);
			if (count == null) {
				// 第一次
				guavaCacheSendCountUtils.put(mail, 1);
			} else {
				// 不是第一次累加，如果过10次，抛出异常，不再发送短信，十分钟后才可再发
				if (count >= sendTotalPerMinute) {
					// 抛出异常，不再发送短信，十分钟后才可再发
					throw new BusinessException("发送邮箱验证码太过频繁，请1分钟后再试");
				} else {
					count++;
					guavaCacheSendCountUtils.put(mail, count);
				}
			}
		}
		{
			// 验证码
			String token = RandomStringUtils.random(6, "1234567890");
			// 存放验证码到 本地内存中
			guavaCacheUtils.put(sendMailCaptchaCallBack, token);
			// 发送验证码
			if (sendMailCaptchaCallBack != null) {
				sendMailCaptchaCallBack.sendCaptchaToMail(token);
			}
		}

	}

	/**
	 * 校验邮箱验证码
	 * 
	 * @param request
	 * @param mail
	 * @param captcha
	 * @return
	 * @author nibili 2016年6月20日
	 * @throws BusinessException
	 */
	public static boolean validMailCaptcha(HttpServletRequest request, String mail, String captcha) throws BusinessException {
		if (ValidUtils.IsEmail(mail) == false) {
			throw new BusinessException("请正确填写邮箱地址");
		}
		if (StringUtils.isNoneBlank(mail, captcha) == false) {
			return false;
		}
		String inerCaptcha = (String) request.getSession().getAttribute(MAIL_CAPTCHA_TAG);
		if (StringUtils.equals(inerCaptcha, captcha.toLowerCase() + "," + mail) == false) {
			// 不匹配
			return false;
		} else {
			request.getSession().setAttribute(MAIL_CAPTCHA_TAG, null);
			return true;
		}
	}

	/**
	 * 发送手机验证码，回调函数
	 * 
	 * @author nibili 2017年4月27日
	 * 
	 */
	public static interface SendTelCaptchaCallBack {
		/**
		 * 这里添加发送手机验证码
		 * 
		 * @param captcha
		 * @author nibili 2017年4月27日
		 */
		public void sendCaptchaToTel(String captcha) throws Exception;
	}

	/**
	 * 发送手机验证码<br/>
	 * 手机验证码有效期为十分钟<br/>
	 * 验证码放在guava中，一分钟内一个号码最多发送几次，可控制
	 * 
	 * @param request
	 * @param mobile
	 * @param sendTotalPerMinute
	 *            一分钟最多发送几次
	 * @throws Exception
	 * @author nibili 2017年4月27日
	 */
	public static void sendTelCaptcha(String mobile, int sendTotalPerMinute, SendTelCaptchaCallBack sendTelCaptchaCallBack) throws Exception {
		if (ValidUtils.IsMobileNumber(mobile) == false) {
			throw new BusinessException("请正确填写手机号码");
		}
		// 验证十分钟内连续发送验证码次数
		{
			Integer count = guavaCacheSendCountUtils.get(mobile, Integer.class);
			if (count == null) {
				// 第一次
				guavaCacheSendCountUtils.put(mobile, 1);
			} else {
				// 不是第一次累加，如果过10次，抛出异常，不再发送短信，十分钟后才可再发
				if (count >= sendTotalPerMinute) {
					// 抛出异常，不再发送短信，十分钟后才可再发
					throw new BusinessException("发送短信太过频繁，请1分钟后再试");
				} else {
					count++;
					guavaCacheSendCountUtils.put(mobile, count);
				}
			}
		}
		{
			// 验证码
			String token = RandomStringUtils.random(6, "1234567890");
			// 存放验证码到 本地内存中
			guavaCacheUtils.put(mobile, token);
			// 发送验证码
			if (sendTelCaptchaCallBack != null) {
				sendTelCaptchaCallBack.sendCaptchaToTel(token);
			}
		}

	}

	/**
	 * 验证手机验证码
	 * 
	 * @param request
	 * @param tel
	 *            电话号码
	 * @param captcha
	 * @return
	 * @author nibili 2016年6月16日
	 */
	public static boolean validTelCaptcha(String tel, String captcha) {
		if (StringUtils.isNoneBlank(tel, captcha) == false) {
			return false;
		}
		String inerCaptcha = guavaCacheUtils.get(tel, String.class);
		if (StringUtils.equals(inerCaptcha, captcha) == false) {
			// 不匹配
			return false;
		} else {
			// 匹配，移除验证码
			guavaCacheUtils.remove(tel);
			return true;
		}
	}

	/**
	 * 发送给客户端验证码
	 * 
	 * @param response
	 * @throws Exception
	 * @auth nibili 2015年8月28日 下午4:05:29
	 */
	public static void sendCaptcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// String token = RandomStringUtils.random(4,
		// "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ");
		String token = RandomStringUtils.random(4, "1234567890");
		request.getSession().setAttribute(CAPTCHA_TAG, token.toLowerCase());
		cage.draw(token, response.getOutputStream());
	}

	/**
	 * 发送给客户端验证码（以json格式返回）
	 * 
	 * @param request
	 * @param response
	 * @throws Exception
	 * @author nibili 2017年9月23日
	 */
	public static void sendCaptchaWithJson(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// String token = RandomStringUtils.random(4,
		// "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ");
		String token = RandomStringUtils.random(4, "1234567890");
		request.getSession().setAttribute(CAPTCHA_TAG, token.toLowerCase());
		ResponseOutputUtils.renderJson(response, MessageDTO.newInstance("", true, token));
	}

	/**
	 * 验正验证码
	 * 
	 * @param clientString
	 * @return
	 * @auth nibili 2015年8月28日 下午4:07:46
	 */
	public static boolean validCaptcha(HttpServletRequest request, String clientString) {
		if (StringUtils.isBlank(clientString) == true) {
			return false;
		}
		String inerCaptcha = (String) request.getSession(true).getAttribute(CAPTCHA_TAG);
		if (StringUtils.equals(inerCaptcha, clientString.toLowerCase()) == false) {
			// 不匹配
			return false;
		} else {
			request.getSession(true).setAttribute(CAPTCHA_TAG, null);
			return true;
		}
	}
}
